package com.autonavi.yunda.yunji.controller;

import com.alibaba.buc.sso.client.util.BucSSOClientUtil;
import com.alibaba.buc.sso.client.util.FilterManager;
import com.alibaba.buc.sso.client.util.SimpleUserUtil;
import com.alibaba.buc.sso.client.vo.BucSSOUser;
import com.alibaba.security.SecurityUtil;
import com.autonavi.yunda.yunji.controller.response.UserInfoResponse;
import com.autonavi.yunda.yunji.core.enums.RoleType;
import com.autonavi.yunda.yunji.service.FactoryUserService;
import com.autonavi.yunda.yunji.service.UserService;
import com.autonavi.yunda.yunji.service.model.FactoryUserDetail;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * BUC相关接口
 */
@RestController
@RequestMapping(value = "/buc", produces = MediaType.APPLICATION_JSON_VALUE)
@Slf4j
class BucController {

    @Value("${spring.buc.backend.url}")
    private String backendUrl;

    @Value("#{'${spring.buc.front.domain.whitelist}'.split(',')}")
    private List<String> domainWhiteList;

    public static final String SSO_LOGIN = "/ssoLogin";
    public static final String SSO_LOGOUT = "/ssoLogout";
    @Resource
    private UserService userService;
    @Resource
    private FactoryUserService factoryUserService;

    /**
     * 获取当前登陆用户信息
     */
    @RequestMapping(value = "/bucsso.do", method = RequestMethod.GET)
    public UserInfoResponse bucsso(HttpServletRequest request) {
        UserInfoResponse response = null;
        try {
            BucSSOUser user = SimpleUserUtil.getBucSSOUser(request);
            response = new UserInfoResponse();
            response.setBucUserData(user);
            if (user.getEmpId() != null) {
                try {
                    FactoryUserDetail factoryUser = factoryUserService.fetchUserInfo(user.getEmpId());
                    response.setFactoryUser(factoryUser);
                } catch (Exception e) {
                    log.warn("Failed to fetch factory user", e);
                }
                RoleType userRole = userService.getAndInitUserRole(user);
                response.setRoleType(userRole);
            }

        } catch (Exception e) {
            log.error("Failed to get SSO info", e);
        }
        return response;
    }

    /**
     * buc登陆成功后跳转后端的接口地址
     * 并根据 frontPage 参数获取前端页面地址进行302跳转
     *
     * @param request
     * @param response
     */
    @RequestMapping(value = "/sso_transfer.do", method = RequestMethod.GET)
    public void sso_transfer(HttpServletRequest request, HttpServletResponse response) {
        String frontPage = request.getParameter("frontPage");

        // 校验地址是否在白名单
//        if (!checkRedirectDomain(frontPage)) {
//            log.error("redirect domain not int the whitelist, url={}", frontPage);
//            directResponse(response, "redirect url not in whitelist.");
//            return;
//        }

        logCookies(request, "sso_transfer.do");
        log.debug("[sso_transfer.do] Set-Cookie response header: {}", response.getHeader("Set-Cookie"));

        log.info("[sso_transfer.do] Redirect to frontPage={}", frontPage);
        try {
            frontPage = SecurityUtil.getSafeUrl(frontPage);
            log.info("[sso_transfer.do] Redirect to frontPage={}", frontPage);
            response.sendRedirect(frontPage);
        } catch (IOException e) {
            log.error("[sso_transfer.do] redirect error, url={}", frontPage, e);
        }
    }

    private void logCookies(HttpServletRequest request, String serviceName) {
        if (null != request.getCookies() && request.getCookies().length > 0) {
            for (Cookie cookie : request.getCookies()) {
                StringBuilder sb = new StringBuilder();
                sb.append("domain:").append(cookie.getDomain())
                        .append("\n secure:").append(cookie.getSecure())
                        .append("\n name:").append(cookie.getName())
                        .append("\n value:").append(cookie.getValue());
                log.debug("[{}] Cookie from request: {}", serviceName, sb);
            }
        } else {
            log.debug("[{}}] No cookie found in request!", serviceName);
        }
    }

    /**
     * 前后端分离项目，前端发现未登录时请求该接口并redirect用户跳转至BUC SSO页面进行登陆
     *
     * @param backUrl 前端传递的成功登陆后的跳转地址
     */
    @RequestMapping(value = SSO_LOGIN, method = RequestMethod.GET)
    public ModelAndView login(@RequestParam(required = false) String backUrl,
                              HttpServletRequest request,
                              HttpServletResponse response) {
        log.info("ssoLogin, backUrl={}", backUrl);

        if (backUrl == null) {
            backUrl = BucSSOClientUtil.getFullUrl(request);
            if (backUrl.endsWith(SSO_LOGIN)) {
                backUrl = backUrl.substring(0, backUrl.length() - SSO_LOGIN.length());
            }
        }

        //校验地址是否在白名单
//        if (!checkRedirectDomain(backUrl)) {
//            log.error("redirect domain not in whitelist, url={}", backUrl);
//            directResponse(response, "redirect url not in whitelist");
//            return null;
//        }

        try {
            backUrl = URLEncoder.encode(backUrl, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("URLEncoder.encode error", e);
        }

        /**
         * 组装 buc 登陆地址
         * 替换前端传递的跳转地址为后端地址，并将前端地址存放至frontPage
         */
        String loginUrl = FilterManager.getSsoServerUrl()
                + "/ssoLogin.htm?APP_NAME=" + FilterManager.getAppName()
                + "&BACK_URL=" + backendUrl + "?frontPage=" + backUrl;

        log.info("ssoLogin redirect url={}", loginUrl);
        try {
            response.sendRedirect(loginUrl);
        } catch (IOException e) {
            log.error("redirect error, url={}", loginUrl, e);
        }
        return null;
    }

    /**
     * 本地登出服务
     *
     * @param backUrl 前端传递成功登出后的跳转地址
     */
    @GetMapping(SSO_LOGOUT)
    public ModelAndView logout(@RequestParam(required = false) String backUrl,
                               HttpServletRequest request, HttpServletResponse response) {
        if (backUrl == null) {
            backUrl = BucSSOClientUtil.getFullUrl(request);
            if (backUrl.endsWith(SSO_LOGOUT)) {
                backUrl = backUrl.substring(0, backUrl.length() - SSO_LOGOUT.length());
            }
        }

        try {
            backUrl = URLEncoder.encode(backUrl, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("URLEncoder.encode error", e);
        }

        try {
            BucSSOClientUtil.handleSSOLogout(request, response);
        } catch (Exception e) {
            log.error("handleSSOLogout error", e);
        }

        String logoutUrl = FilterManager.getSsoServerUrl()
                + "/ssoLogout.htm?APP_NAME=" + FilterManager.getAppName()
                + "&BACK_URL=" + backUrl;

        try {
            response.sendRedirect(logoutUrl);
        } catch (IOException e) {
            log.error("redirect error, url={}", logoutUrl, e);
        }
        return null;
    }

    /**
     * 获取 url 中的主机名
     */
    private String getHost(String httpUrl) throws MalformedURLException {
        URL url = new URL(httpUrl);
        return url.getHost();
    }

    /**
     * 校验跳转url是否在白名单
     */
    private Boolean checkRedirectDomain(String redirectUrl) {
        String host;
        try {
            String redirectUrlDecode = URLDecoder.decode(redirectUrl, "UTF-8");
            host = getHost(redirectUrlDecode);
        } catch (Exception e) {
            log.error("跳转地址不正确, url={}", redirectUrl);
            return false;
        }

        List<String> collect = domainWhiteList.stream()
                .filter(domain -> StringUtils.equals(domain, host))
                .limit(1)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(collect)) {
            String msg = String.format("domain=%s  whiteList=%s",
                    host, Arrays.toString(domainWhiteList.toArray()));
            log.error(msg);
            return false;
        }
        return true;
    }

    /**
     * 跳转页面直接响应
     */
    private void directResponse(HttpServletResponse response, String msg) {
        try {
            response.getOutputStream().println(msg);
        } catch (IOException e) {
            log.error("Failed to get output stream", e);
        }
    }
}

